#define _DEFAULT_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

#include "node.h"

#define MIN(a,b) (a) < (b) ? (a) : (b)

void node_display(Node *first)
{
    Node *p = first;
    puts("--------------Node list--------------");
    if (!p)
        puts("[EMPTY]");
    else
        for (; p; p = p->next)
            printf("ip = \"%s\"  mac = \"%s\"\n", p->ip, p->mac);
    puts("-------------------------------------");
}

void node_save(Node *first, const char *fpath)
{
    FILE *stream = fopen(fpath, "wb");
    if (!stream)
    {
        perror("fopen");
        exit(EXIT_FAILURE);
    }
    for (Node *p = first; p; p = p->next)
    {
        printf("Writing 1 node to file: %s %s\n", p->ip, p->mac);
        fwrite(p, sizeof(Node), 1, stream);
    }
    fclose(stream);
}

void node_destroy(Node *list)
{
    Node *p = list;
    Node *q = NULL;
    for (; p; p = p->next, free(q))
        q = p;
}

Node *node_new(const char *ip, const char *mac)
{
    Node *result = malloc(sizeof(Node));
    memset(result, 0, sizeof(Node));
    memcpy(result->ip, ip, MIN(strlen(ip), IP_SIZE));
    memcpy(result->mac, mac, MIN(strlen(mac), IP_SIZE));

    return result;
}

Node *node_add(Node *first, Node *n)
{
    if (first == NULL)
        return n;
    Node *p = first;
    for (; p->next; p = p->next);
    p->next = n;
    return first;
}

Node *node_list_from_buffer(const char *buf, size_t nbytes)
{
    size_t node_size = sizeof(Node);
    if (nbytes == 0)
        return NULL;
    assert(nbytes % node_size == 0);
    //Do 1 iteration explicitly
    Node *p = node_new(buf + offsetof(Node, ip), buf + offsetof(Node, mac));
    Node *first = p;                                //To do this
    size_t offset = node_size;
    for (;offset < nbytes; offset += node_size)
    {
        puts("Hit alloc");
        p->next = node_new(buf + offset + offsetof(Node, ip), buf + offset + offsetof(Node, mac));
        p = p->next;
    }
    p->next = NULL;
    return first;
}

Node *node_read_from_fd(int fd, size_t nbytes)
{
    char *readbuf = malloc(nbytes + 1);
    memset(readbuf, 0, nbytes + 1);

    if (read(fd, readbuf, nbytes) == -1)
    {
        perror("read");
        close(fd);
        free(readbuf);
        exit(EXIT_FAILURE);
    }

    Node *nodes = node_list_from_buffer(readbuf, nbytes);

    close(fd);
    free(readbuf);
    return nodes;
}


#define MODE_CREAT 0644
Node *node_read(const char *fpath)
{
    int fd;
    if ((fd = open(fpath, O_RDONLY | O_CREAT, MODE_CREAT)) == -1)
    {
        perror("open");
        goto fail;
    }

    struct stat statbuf = {0};
    if (stat(fpath, &statbuf) == -1)
    {
        perror("stat");
        goto stat_fail;
    }

    size_t nbytes = (size_t) statbuf.st_size;
    if (nbytes == 0)
    {
        close(fd);
        return NULL;
    }
    assert(nbytes % sizeof(Node) == 0);

    return node_read_from_fd(fd, nbytes);

fail:
    if (fd > 0) close(fd);
stat_fail:
    exit(EXIT_FAILURE);
}
